package com.ybwh.springboot2.seq.service.impl;

import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import com.baomidou.lock.executor.RedissonLockExecutor;
import com.google.common.base.Preconditions;
import com.ybwh.springboot2.common.redis.RedisClient;
import com.ybwh.springboot2.common.redis.RedisKeys;
import com.ybwh.springboot2.seq.mapper.SeqNoMapper;
import com.ybwh.springboot2.seq.model.entity.SeqNo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

/**
 * <p>
 * 业务序列号信息服务接口实现类
 * </p>
 *
 * @author youbeiwuhuan
 * @version V1.0
 * @since 2023-06-07 11:50:26
 */
@Slf4j
@Component
public class BusiNoGenerator {

    private static final int BACTH_SIZE = 10000;


    @Autowired
    private RedisClient redisClient;


    @Autowired
    private LockTemplate lockTemplate;
    @Resource
    private SeqNoMapper seqNoMapper;

    @Transactional(rollbackFor = Exception.class)
    public synchronized void bacthGenNo(String preffix, int seqLen) {
        Preconditions.checkArgument(StringUtils.isNotBlank(preffix));
        Preconditions.checkArgument(seqLen > 0);

        // 获取锁
        final LockInfo lockInfo = lockTemplate.lock(RedisKeys.getBusinoKey(preffix) + "-LOCK", 30000L, 5000L, RedissonLockExecutor.class);
        if (null == lockInfo) {
            throw new RuntimeException("业务处理中,请稍后再试");
        }
        // 获取锁成功，处理业务
        try {
            while (true) {
                if (hasNoInCache(preffix)) {
                    break;
                }

                Long now = System.currentTimeMillis();
                SeqNo seqInfo = seqNoMapper.selectBySeqCode(preffix);
                if (null == seqInfo) {
                    try {
                        seqInfo = new SeqNo();
                        seqInfo.setSeqCode(preffix);
                        seqInfo.setSeqLen(seqLen);
                        seqInfo.setCreateTime(now);
                        seqInfo.setUpdateTime(now);
                        seqNoMapper.saveIgnoreNull(seqInfo);
                    } catch (DuplicateKeyException e) {
                    }

                    continue;
                }


                // 批量取号
                long seq = seqInfo.getSeq();
                int effect = seqNoMapper.updateSeq(preffix, seqInfo.getSeq() + BACTH_SIZE, seqInfo.getSeq(), now);
                if (0 == effect) {
                    continue;
                }


                // 将批量取到的号存到redis
                for (int i = 1; i < BACTH_SIZE + 1; i++) {
                    String no = preffix + String.format("%0" + seqLen + "d", seq + i);
                    addBusiNo2Cache(preffix, no);
                }
            }

        } finally {
            //释放锁
            lockTemplate.releaseLock(lockInfo);
        }
        //结束
    }


    public boolean hasNoInCache(String preffix) {
        Preconditions.checkArgument(StringUtils.isNotBlank(preffix));
        return redisClient.lLen(RedisKeys.getBusinoKey(preffix)) > 0;
    }

    private void addBusiNo2Cache(String preffix, String no) {
        redisClient.lPush(RedisKeys.getBusinoKey(preffix), no);
    }

    /**
     * 从缓存中取数据
     *
     * @param preffix
     * @return
     */
    public String getBusiNoFromCache(String preffix) {
        Preconditions.checkArgument(StringUtils.isNotBlank(preffix));

        return redisClient.rPop(RedisKeys.getBusinoKey(preffix));
    }
}
